package org.tigris.subversion.subclipse.graph.editors;

import java.util.Arrays;
import java.util.List;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.gef.ContextMenuProvider;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.MouseWheelHandler;
import org.eclipse.gef.MouseWheelZoomHandler;
import org.eclipse.gef.RootEditPart;
import org.eclipse.gef.editparts.ScalableRootEditPart;
import org.eclipse.gef.editparts.ZoomManager;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.ZoomInAction;
import org.eclipse.gef.ui.actions.ZoomOutAction;
import org.eclipse.gef.ui.parts.GraphicalEditor;
import org.eclipse.jface.action.IAction;
import org.eclipse.swt.SWT;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.tigris.subversion.subclipse.graph.Activator;
import org.tigris.subversion.subclipse.graph.cache.Cache;
import org.tigris.subversion.subclipse.graph.cache.WorkListener;
import org.tigris.subversion.svnclientadapter.ISVNClientAdapter;
import org.tigris.subversion.svnclientadapter.ISVNLogMessage;
import org.tigris.subversion.svnclientadapter.ISVNLogMessageCallback;
import org.tigris.subversion.svnclientadapter.SVNClientException;

public class RevisionGraphEditor extends GraphicalEditor {

  private OverviewOutlinePage overviewOutlinePage;

  private ActionRegistry actionRegistry;

  public static final String SHOW_DELETED_PREFERENCE = "RevisionGraph_showDeleted";
  public static final String FILTER_CONNECTIONS = "RevisionGraph_filterConnections";
  public static final String CHRONOLOGICAL = "RevisionGraph_chronological";
  public static final int SHOW_DELETED_MODIFIED = 0;
  public static final int SHOW_DELETED_YES = 1;
  public static final int SHOW_DELETED_NO = 2;

  public RevisionGraphEditor() {
    super();
    setEditDomain(new RevisionGraphEditDomain(this));
  }

  public ActionRegistry getActionRegistry() {
    if (actionRegistry == null) actionRegistry = new ActionRegistry();
    return actionRegistry;
  }

  public void setFocus() {}

  public void showGraphFor(IResource resource) {
    setPartName(resource.getName() + " revision graph");
    GraphBackgroundTask task =
        new GraphBackgroundTask(getSite().getPart(), getGraphicalViewer(), this, resource);
    try {
      task.run();
    } catch (Exception e) {
      Activator.handleError(e);
      Activator.showErrorDialog("Show Revision Graphp for " + resource.getName(), e);
    }
  }

  public void showGraphFor(RevisionGraphEditorInput editorInput) {
    setPartName(editorInput.getName() + " revision graph");
    GraphBackgroundTask task;
    if (editorInput.getResource() == null)
      task =
          new GraphBackgroundTask(
              getSite().getPart(), getGraphicalViewer(), this, editorInput.getRemoteResource());
    else
      task =
          new GraphBackgroundTask(
              getSite().getPart(), getGraphicalViewer(), this, editorInput.getResource());
    try {
      task.run();
    } catch (Exception e) {
      Activator.handleError(e);
      Activator.showErrorDialog("Show Revision Graphp for " + editorInput.getName(), e);
    }
  }

  public Object getAdapter(Class adapter) {
    if (adapter == GraphicalViewer.class || adapter == EditPartViewer.class) {
      return getGraphicalViewer();
    } else if (adapter == ZoomManager.class) {
      return ((ScalableRootEditPart) getGraphicalViewer().getRootEditPart()).getZoomManager();
    } else if (adapter == IContentOutlinePage.class) {
      return getOverviewOutlinePage();
    }
    return super.getAdapter(adapter);
  }

  public void refresh() {
    getGraphicalViewer().setContents("Loading graph... This can take several minutes");
    showGraphFor((RevisionGraphEditorInput) getEditorInput());
  }

  public void doSave(IProgressMonitor monitor) {}

  public void doSaveAs() {}

  public void init(IEditorSite site, IEditorInput input) throws PartInitException {
    setSite(site);
    setInput(input);
  }

  public boolean isDirty() {
    return false;
  }

  public boolean isSaveAsAllowed() {
    return false;
  }

  public GraphicalViewer getViewer() {
    return getGraphicalViewer();
  }

  protected OverviewOutlinePage getOverviewOutlinePage() {
    if (null == overviewOutlinePage && null != getGraphicalViewer()) {
      RootEditPart rootEditPart = getGraphicalViewer().getRootEditPart();
      if (rootEditPart instanceof ScalableRootEditPart) {
        overviewOutlinePage = new OverviewOutlinePage((ScalableRootEditPart) rootEditPart);
      }
    }
    return overviewOutlinePage;
  }

  protected void configureGraphicalViewer() {
    super.configureGraphicalViewer();
    GraphicalViewer viewer = getGraphicalViewer();
    ScalableRootEditPart root = new ScalableRootEditPart();
    viewer.setRootEditPart(root);
    viewer.setEditPartFactory(new GraphEditPartFactory(viewer));
    viewer.setContents("Loading graph... This can take several minutes");
    ContextMenuProvider cmProvider = new RevisionGraphMenuProvider(viewer, this);
    viewer.setContextMenu(cmProvider);
    getSite().setSelectionProvider(viewer);
    IEditorInput input = getEditorInput();
    if (input instanceof FileEditorInput) {
      FileEditorInput fileEditorInput = (FileEditorInput) input;
      showGraphFor(fileEditorInput.getFile());
    } else if (input instanceof RevisionGraphEditorInput) {
      RevisionGraphEditorInput editorInput = (RevisionGraphEditorInput) input;
      showGraphFor(editorInput);
    }

    // zoom stuff
    ZoomManager zoomManager = ((ScalableRootEditPart) viewer.getRootEditPart()).getZoomManager();
    IAction zoomIn = new ZoomInAction(zoomManager);
    IAction zoomOut = new ZoomOutAction(zoomManager);
    getActionRegistry().registerAction(zoomIn);
    getActionRegistry().registerAction(zoomOut);
    // keyboard
    getSite().getKeyBindingService().registerAction(zoomIn); // FIXME, deprecated
    getSite().getKeyBindingService().registerAction(zoomOut); // FIXME, deprecated
    List zoomContributions =
        Arrays.asList(
            new String[] {ZoomManager.FIT_ALL, ZoomManager.FIT_HEIGHT, ZoomManager.FIT_WIDTH});
    zoomManager.setZoomLevelContributions(zoomContributions);
    // mouse wheel
    viewer.setProperty(
        MouseWheelHandler.KeyGenerator.getKey(SWT.MOD1), MouseWheelZoomHandler.SINGLETON);
  }

  protected void initializeGraphicalViewer() {}
}

class WorkMonitorListener implements WorkListener {

  private IProgressMonitor monitor;
  private int unitWork;

  public WorkMonitorListener(IProgressMonitor monitor, int unitWork) {
    this.monitor = monitor;
    this.unitWork = unitWork;
  }

  public void worked() {
    monitor.worked(unitWork);
  }
}

class CallbackUpdater implements ISVNLogMessageCallback {

  private Cache cache;
  private IProgressMonitor monitor;
  private int unitWork;
  private ISVNClientAdapter client;
  private boolean canceled;

  public CallbackUpdater(
      Cache cache, IProgressMonitor monitor, int unitWork, ISVNClientAdapter client) {
    this.cache = cache;
    this.monitor = monitor;
    this.unitWork = unitWork;
    this.client = client;
  }

  public void singleMessage(ISVNLogMessage message) {
    if (!canceled && monitor.isCanceled()) {
      try {
        canceled = true;
        client.cancelOperation();
      } catch (SVNClientException e) {
      }
      return;
    }
    cache.update(message);
    monitor.worked(unitWork);
  }
}
